package com.senseauto.aicamera;

import android.Manifest;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.Context;
import android.content.DialogInterface;
import android.content.pm.PackageManager;
import android.content.res.Configuration;
import android.graphics.ImageFormat;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraAccessException;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CameraMetadata;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.Image;
import android.media.ImageReader;
import android.media.MediaCodec;
import android.media.MediaRecorder;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.text.TextUtils;
import android.util.Log;
import android.util.Size;
import android.util.SparseIntArray;
import android.view.Surface;
import android.view.TextureView;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;

import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;

import com.senseauto.aicamera.utils.AutoFitTextureView;
import com.senseauto.aicamera.utils.FileUtils;
import com.senseauto.aicamera.utils.TimeUtil;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;


public class VideoRecord2Activity extends AppCompatActivity {
    private static final String TAG = "VideoRecordActivity";
 
    private Context mContext;
    private CameraManager mCameraManager;
    private int mFacingFront = 2;
    private int mCurrentFacing = mFacingFront;
 
    private CameraDevice mCurrentCameraDevice;
    private CaptureRequest mCurrentPreviewRequest;
    private TextureView mTextureView;
    private Surface mPreviewSurface;
    private CameraCaptureSession mCurrentCameraCaptureSession;
 
    private Button mBtnTakePicture;
    private int previewWidth = 1920;
    private int previewHeight = 1080;
    private Size mVideoSize ;

    private boolean isRecording = false;
    private MediaRecorder mMediaRecorder;
    private Surface mRecordSurface;
    private CaptureRequest mVideoRecordRequest;
    private String mVideoFilePath = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM) + "/video.mp4";
    HandlerThread mBackgroundThread;
    Handler mBackgroundHandler;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_video_record);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        this.mContext = this;
        if (getIntent() != null) {
            String cameraIdStr = getIntent().getStringExtra("dmsCameraID");
            if (TextUtils.isEmpty(cameraIdStr)) {
                cameraIdStr = "2";
            }
            Log.e(TAG, "getIntent() != null cameraIdStr="+cameraIdStr);
            mFacingFront = Integer.parseInt(cameraIdStr);
            previewWidth = Integer.parseInt(getIntent().getStringExtra("width" ));
            previewHeight =Integer.parseInt( getIntent().getStringExtra("height" ));
            mVideoSize = new Size(previewWidth,previewHeight);
        } else {
            mFacingFront = 0;
            previewWidth = 1920;
            previewHeight = 1080;
            mVideoSize = new Size(1920,1080);
        }
        initView();
        initCamera();
        startBackgroundThread();
     }
    /**
     * Starts a background thread and its {@link Handler}.
     */
    private void startBackgroundThread() {
        mBackgroundThread = new HandlerThread("CameraBackground");
        mBackgroundThread.start();
        mBackgroundHandler = new Handler(mBackgroundThread.getLooper());
    }

    /**
     * Stops the background thread and its {@link Handler}.
     */
    private void stopBackgroundThread() {
        mBackgroundThread.quitSafely();
        try {
            mBackgroundThread.join();
            mBackgroundThread = null;
            mBackgroundHandler = null;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        stopBackgroundThread();
        closeCamera();
    }

    private void initView() {
        mTextureView = findViewById(R.id.texture);
        mTextureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
            @RequiresApi(api = Build.VERSION_CODES.M)
            @Override
            public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surface, int width, int height) {
                // 这里解释一下，为什么摄像头打开的时候没有马上打开预览，因为预览画面的展示需要 surface 承载，所以需要等 surface 准备好才行
                surface.setDefaultBufferSize(mVideoSize.getWidth(),mVideoSize.getHeight());
                // 预览所需的 previewSurface
                mPreviewSurface = new Surface(surface);

            }
 
            @Override
            public void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surface, int width, int height) {
 
            }
 
            @Override
            public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface) {
                mPreviewSurface.release();
                mPreviewSurface = null;
                return false;
            }
 
            @Override
            public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) {
 
            }
        });
 
        mBtnTakePicture = findViewById(R.id.video);
        mBtnTakePicture.setOnClickListener(new View.OnClickListener() {
            @RequiresApi(api = Build.VERSION_CODES.M)
            @Override
            public void onClick(View v) {
                Log.d(TAG, "onClick: 点击拍照");
                if(isRecording){
                    stopRecording();
                    mBtnTakePicture.setText("录制");
                }else {
                    startRecording();
                    mBtnTakePicture.setText("停止");
                }
            }
        });
    }
 
    private void initCamera() {
        // 1. 先获取 CameraManager
        mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
        if (mCameraManager == null) {
            return;
        }
        /*try {
            for (String cameraId : mCameraManager.getCameraIdList()) {
                CameraCharacteristics characteristics = mCameraManager.getCameraCharacteristics(cameraId);
                int facing = characteristics.get(CameraCharacteristics.LENS_FACING);
                if (facing == CameraCharacteristics.LENS_FACING_FRONT) {
                    mFacingFront = Integer.parseInt(cameraId);
                    int supportLEVEL = characteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
                     Log.d(TAG, "testCamera2: 当前是前摄，supportLEVEL: " + supportLEVEL);
                } else if (facing == CameraCharacteristics.LENS_FACING_BACK) {
                    mFacingBack = Integer.parseInt(cameraId);
                    int supportLEVEL = characteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
                     Log.d(TAG, "testCamera2: 当前是后摄，supportLEVEL: " + supportLEVEL);
                }
            }
        } catch (CameraAccessException e) {
            Log.e(TAG, "testCamera2: ", e);
        }*/
    }
 
    private void openCamera(){
        closeCamera();
        Log.e(TAG, "openCamera");
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
            return;
        }
        try {
            // 2. 调用 openCamera，打开摄像头
            mCameraManager.openCamera(String.valueOf(mCurrentFacing), new CameraDevice.StateCallback() {
                @RequiresApi(api = Build.VERSION_CODES.M)
                @Override
                public void onOpened(@NonNull CameraDevice camera) {
                     Log.d(TAG, "onOpened: 摄像头链接成功, facing: " + mCurrentFacing);
                    // 摄像头成功打开
                    mCurrentCameraDevice = camera;
                    // 准备视频录制时所需的 recordSurface
                    prepareMediaRecorder();
                    startPreview();
                }
 
                @Override
                public void onDisconnected(@NonNull CameraDevice camera) {
                     Log.d(TAG, "onDisconnected: 摄像头链接断开");
                }
 
                @Override
                public void onError(@NonNull CameraDevice camera, int error) {
                     Log.d(TAG, "onError: 摄像头打开失败, error: " + error);
                    camera.close();
                }
            }, null);
        } catch (CameraAccessException e) {
            Log.e(TAG, "openCamera: ", e);
        }
    }
 
    private void startPreview(){
        Log.e(TAG, "startPreview()");
        if(mCurrentCameraDevice == null){
            return;
        }
        if(mPreviewSurface == null){
            return;
        }
        try {
            // 3. 创建session，可以看到这里绑定了两个 surface，一个用于承载预览显示，一个用于视频录制
            List<Surface> mOutputs = new ArrayList<>();
            mOutputs.add(mPreviewSurface);
            mOutputs.add(mRecordSurface);
            mCurrentCameraDevice.createCaptureSession(mOutputs, new CameraCaptureSession.StateCallback() {
                @Override
                public void onConfigured(@NonNull CameraCaptureSession session) {
                    Log.e(TAG, "createCaptureSession onConfigured ");
                    mCurrentCameraCaptureSession = session;
                    try {
                        // 4. 开启预览
                        createPreviewCaptureRequest();
                        mCurrentCameraCaptureSession.setRepeatingRequest(mCurrentPreviewRequest, null, mBackgroundHandler);
                    } catch (CameraAccessException e) {
                         Log.e(TAG, "onConfigured: ", e);
                    }
                }
 
                @Override
                public void onConfigureFailed(@NonNull CameraCaptureSession session) {
                    Log.e(TAG, "createCaptureSession onConfigureFailed ");

                }
            }, null);
        } catch (CameraAccessException e) {
             Log.e(TAG, "startPreview: ", e);
        }
    }
 
    /**
     * 用于预览显示的request
     */
    private void createPreviewCaptureRequest() {
        try {
            Log.e(TAG, "createPreviewCaptureRequest");
            CaptureRequest.Builder mCurrentPreviewRequestBuilder = mCurrentCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
            mCurrentPreviewRequestBuilder.addTarget(mPreviewSurface);
            mCurrentPreviewRequest = mCurrentPreviewRequestBuilder.build();
        } catch (CameraAccessException e) {
             Log.e(TAG, "createPreviewCaptureRequest: ", e);
        }
    }
 
    /**
     * 用于录制视频的request
     */
    private void createVideoRecordCaptureRequest(){
        try {
            Log.e(TAG, "createVideoRecordCaptureRequest");

            CaptureRequest.Builder builder = mCurrentCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
            // 用于承载录制视频流
            builder.addTarget(mRecordSurface);
            // 用于承载预览显示流
            builder.addTarget(mPreviewSurface);
            mVideoRecordRequest = builder.build();
        } catch (CameraAccessException e) {
             Log.e(TAG, "createVideoRecordCaptureRequest: ", e);
        }
    }
 
    private void closeCamera(){
        if(mCurrentCameraDevice != null){
            mCurrentCameraDevice.close();
            mCurrentCameraDevice = null;
        }
    }
 
    @Override
    protected void onResume() {
        super.onResume();
        openCamera();
    }
 
    @Override
    protected void onPause() {
        super.onPause();
        closeCamera();
        if(mMediaRecorder != null){
            mMediaRecorder.release();
            mMediaRecorder = null;
        }
        if(mRecordSurface != null){
            mRecordSurface.release();
            mRecordSurface = null;
        }
    }
 
    @RequiresApi(api = Build.VERSION_CODES.M)
    private void prepareMediaRecorder(){
        try {
            // 这里很重要的一个点 mediaRecorder 的复用，不需要每次重新 new 一个新的
            if(mMediaRecorder == null){
                mMediaRecorder = new MediaRecorder();
            }else {
                // 如果 mMediaRecorder 存在，只需要重新 reset 就行
                mMediaRecorder.reset();
            }
            // surface 通过 MediaCodec.createPersistentInputSurface() 创建，
            // 然后通过 setInputSurface 的方式设置给 MediaRecorder
            // 这个 surface 也会在 createSession 时和 previewSurface 一起传入，这样开始录制时就不需要重新创建 session
            if(mRecordSurface == null){
                mRecordSurface = MediaCodec.createPersistentInputSurface();
            }
            mMediaRecorder.setInputSurface(mRecordSurface);
            mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
            mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.SURFACE);
            mMediaRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
            mMediaRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
            mMediaRecorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264);
            // 设置视频文件地址，需要读写权限
            mMediaRecorder.setOutputFile(mVideoFilePath);
            Log.e(TAG, "mVideoFilePath="+mVideoFilePath);
            mMediaRecorder.setVideoSize(mVideoSize.getWidth(), mVideoSize.getHeight());
            mMediaRecorder.setVideoFrameRate(25);
            // prepare 执行之后 才能使用
            mMediaRecorder.prepare();
        } catch (IOException e) {
             Log.e(TAG, "prepareMediaRecorder: ", e);
        }
    }
 
    @RequiresApi(api = Build.VERSION_CODES.M)
    private void startRecording(){
        isRecording = true;
        try {
            // 5. 开启录制
            // 先准备 mediaRecorder
            prepareMediaRecorder();
            // 创建录制视频用的 request
            createVideoRecordCaptureRequest();
            // 先将 PreviewRequest 停掉，因为 PreviewRequest 只绑定了一个previewSurface target，录制的时候还需要绑定 recordSurface target
            mCurrentCameraCaptureSession.stopRepeating();
            // 开始 recordRequest 请求
            mCurrentCameraCaptureSession.setRepeatingRequest(mVideoRecordRequest, null, mBackgroundHandler);
            // 开始录制
            mMediaRecorder.start();
        } catch (CameraAccessException e) {
             Log.e(TAG, "startRecording: ", e);
        }
    }
 
    private void stopRecording(){
        isRecording = false;
        // 6. 结束录制
        if(mMediaRecorder != null){
            // 停止录制
            mMediaRecorder.stop();
        }
        if(mCurrentCameraCaptureSession != null){
            try {
                // 先将 RecordRequest 请求停掉，因为录制停止之后，只需要一个预览流即可，降低功耗
                mCurrentCameraCaptureSession.stopRepeating();
                mCurrentCameraCaptureSession.setRepeatingRequest(mCurrentPreviewRequest, null, mBackgroundHandler);
            } catch (CameraAccessException e) {
                 Log.e(TAG, "stopRecording: ", e);
            }
        }
    }
} 